﻿using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Script.Serialization;
using System.Collections.Generic;
using Microsoft.Office.Server.UserProfiles;

namespace GoogleLatitude2SharePoint.LatitudeWebPart
{
    [ToolboxItemAttribute(false)]
    public class LatitudeWebPart : Microsoft.SharePoint.WebPartPages.WebPart
    {

        public static DateTime ConvertFromUnixTimestamp(double timestamp)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return origin.AddSeconds(timestamp);
        }

        private const int BUFFER_SIZE = 1024;

        private static void Download(string url, Stream writeStream)
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
            var response = request.GetResponse();

            var readStream = response.GetResponseStream();

            var data = new Byte[BUFFER_SIZE];

            int n;
            do
            {
                n = readStream.Read(data, 0, BUFFER_SIZE);
                writeStream.Write(data, 0, n);
            } while (n > 0);

            writeStream.Flush();
            readStream.Close();
        }

        public int mapWidth = 600;
        public int mapHeight = 400;
        public string currentMapType = MapTypes.Road.ToString();
        public string currentMode = ModeTypes.SingleUser.ToString();
        public string currentDataSource = DataSourceTypes.Manual.ToString();
        public string currentUser = UserTypes.Current.ToString();
        public string currentUsers = UsersTypes.Site.ToString();

        public string BingMapsKey { get; set; }
        public string LatitudeUserID { get; set; }
        public string PropertyName { get; set; }
        public string SourceSite { get; set; }
        public string SourceList { get; set; }
        public string SourceField { get; set; }
        public string SourceGroup { get; set; }
        public string CurrentUserLoginName { get; set; }
        public string ManualDisplayName { get; set; }
        public string SourceNameField { get; set; }


        public class PushPin
        {
            private string name = string.Empty;

            public decimal? Latitude { get; set; }
            public decimal? Longitude { get; set; }
            public DateTime Date { get; set; }
            public string Name 
            {
                get
                {
                    return name;
                }
                set
                {
                    name = value;
                }
            }

            public string PictureURL { get; set; }

            public PushPin()
            {
            }
        }

        public List<PushPin> PushPins
        {
            get
            {
                List<PushPin> pushPins = new List<PushPin>();

                switch (CurrentMode)
                {
                    case ModeTypes.SingleUser:

                        string userID = string.Empty;
                        string userName = string.Empty;
                        string pictureUrl = string.Empty;

                        switch (CurrentDataSource)
                        {
                            case DataSourceTypes.Manual:

                                userID = LatitudeUserID;
                                userName = ManualDisplayName;

                                break;
                            
                            case DataSourceTypes.UserProfile:

                                UserProfileManager profileManager = new UserProfileManager(SPServiceContext.Current);

                                UserProfile profile;

                                string loginName = string.Empty;

                                switch (CurrentUser)
                                {


                                    case UserTypes.Current:

                                        loginName = SPContext.Current.Web.CurrentUser.LoginName;
                                        userName = SPContext.Current.Web.CurrentUser.Name;

                                        break;
                                    case UserTypes.Manual:
                                        loginName = CurrentUserLoginName;
                                        userName = CurrentUserLoginName;
                                        break;
                                    case UserTypes.SiteOwner:
                                        loginName = SPContext.Current.Web.SiteAdministrators[0].LoginName;
                                        userName = SPContext.Current.Web.SiteAdministrators[0].Name;
                                        break;

                                }

                                if (!string.IsNullOrEmpty(loginName) && profileManager.UserExists(loginName))
                                {
                                    profile = profileManager.GetUserProfile(loginName);

                                    if (profile[PropertyName] != null && profile[PropertyName].Value != null)
                                        userID = profile[PropertyName].Value.ToString();

                                    if (profile["PictureURL"] != null && profile["PictureURL"].Value != null)
                                        pictureUrl = profile["PictureURL"].Value.ToString();

                                }


                                break;

                        }


                        if (!string.IsNullOrEmpty(userID))
                        {
                            try
                            {
                                PushPin pushPin = GetLatitudeData(userID);


                                pushPin.Name = userName;
                                pushPin.PictureURL = pictureUrl;

                                pushPins.Add(pushPin);
                            }
                            catch (Exception) { }
                        }

                        break;

                    case ModeTypes.MultipleUser:


                        switch (CurrentDataSource)
                        {
                            case DataSourceTypes.List:

                                using (SPWeb web = SPContext.Current.Site.OpenWeb(new Guid(SourceSite)))
                                {
                                    SPListItemCollection listItems = web.Lists[new Guid(SourceList)].Items;

                                    foreach (SPListItem item in listItems)
                                    {
                                        Guid guidField = new Guid(SourceField);
                                        Guid guidNameField = new Guid(SourceNameField);

                                        if (item[guidField] != null)
                                        {
                                            try
                                            {
                                                PushPin pushPin = GetLatitudeData(item[guidField].ToString());

                                                if(item[guidNameField] != null)
                                                    pushPin.Name = item[guidNameField].ToString();

                                                pushPins.Add(pushPin);

                                            }
                                            catch (Exception) { }

                                        }
                                    }
                                }


                                
                                break;
                            
                            
                            case DataSourceTypes.UserProfile:

                                SPUserCollection users = null;

                                UserProfileManager profileManager = new UserProfileManager(SPServiceContext.Current);

                                switch (CurrentUsers)
                                {
                                    case UsersTypes.Site:

                                        using (SPWeb web = SPContext.Current.Site.OpenWeb(new Guid(SourceSite)))
                                            users = web.AllUsers;
                                        
                                        break;
                                    case UsersTypes.Group:

                                        using (SPWeb web = SPContext.Current.Site.OpenWeb(new Guid(SourceSite)))
                                            users = web.Groups.GetByID(int.Parse(SourceGroup)).Users;

                                        break;

                                }


                                foreach (SPUser user in users)
                                {

                                    if (profileManager.UserExists(user.LoginName))
                                    {
                                        UserProfile profile = profileManager.GetUserProfile(user.LoginName);

                                        if (profile[PropertyName] != null && profile[PropertyName].Value != null)
                                        {
                                            try
                                            {
                                                PushPin pushPin = GetLatitudeData(profile[PropertyName].Value.ToString());

                                                pushPin.Name = user.Name;

                                                if (profile["PictureURL"] != null && profile["PictureURL"].Value != null)
                                                    pushPin.PictureURL = profile["PictureURL"].Value.ToString();

                                                pushPins.Add(pushPin);

                                            }

                                            catch (Exception) { }
                                        }
                                    }

                                }


                                break;

                        }


                        
                        break;
                }






                return pushPins;
            }
        }

        public DataSourceTypes CurrentDataSource
        {
            get
            {
                return (DataSourceTypes)Enum.Parse(typeof(DataSourceTypes), currentDataSource);
            }
            set
            {
                currentDataSource = value.ToString();
            }
        }


        public MapTypes CurrentMapType
        {
            get
            {
                return (MapTypes)Enum.Parse(typeof(MapTypes), currentMapType);
            }
            set
            {
                currentMapType = value.ToString();
            }
        }

        public ModeTypes CurrentMode
        {
            get
            {
                return (ModeTypes)Enum.Parse(typeof(ModeTypes), currentMode);
            }
            set
            {
                currentMode = value.ToString();
            }
        }

        public UserTypes CurrentUser
        {
            get
            {
                return (UserTypes)Enum.Parse(typeof(UserTypes), currentUser);
            }
            set
            {
                currentUser = value.ToString();
            }
        }

        public UsersTypes CurrentUsers
        {
            get
            {
                return (UsersTypes)Enum.Parse(typeof(UsersTypes), currentUsers);
            }
            set
            {
                currentUsers = value.ToString();
            }
        }


        public int MapWidth 
        {
            get
            {
                return mapWidth;
            }
            set
            {
                mapWidth = value;
            }
        }

        public int MapHeight
        {
            get
            {
                return mapHeight;
            }
            set
            {
                mapHeight = value;
            }
        }

        public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
        {
            List<ToolPart> toolParts = new List<ToolPart>();

            toolParts.Add(new LatitudeWebPartToolPart());

            foreach (ToolPart toolPart in base.GetToolParts())
                toolParts.Add(toolPart);


            return toolParts.ToArray();
        }

        private PushPin GetLatitudeData(string id)
        {
            PushPin pushPin = new PushPin();

            // Url of the JSON Latitude feed
            var url = String.Format("http://www.google.com/latitude/apps/badge/api?user={0}&type=json", id);

            // We download the file
            var ms = new MemoryStream();
            Download(url, ms);

            // JSON in text format
            var textContent = UTF8Encoding.UTF8.GetString(ms.ToArray());

            // We convert the JSON text file to an object
            // It returns 
            var jss = new JavaScriptSerializer();
            var jsonContent = jss.DeserializeObject(textContent) as Dictionary<String, Object>;

            // We get the data
            var features = (jsonContent["features"] as object[])[0] as Dictionary<string, object>;
            var geometry = features["geometry"] as Dictionary<string, object>;
            var coordinates = geometry["coordinates"] as object[];
            pushPin.Longitude = coordinates[0] as decimal?;
            pushPin.Latitude = coordinates[1] as decimal?;

            // And then the timestamp
            var properties = features["properties"] as Dictionary<string, object>;
            var date = ConvertFromUnixTimestamp((double)(int)properties["timeStamp"]);

            // We convert the UTC date to local time
            pushPin.Date = date.ToLocalTime();


            return pushPin;
        }

        protected override void CreateChildControls()
        {

            if (string.IsNullOrEmpty(BingMapsKey) || 
                (string.IsNullOrEmpty(LatitudeUserID) && CurrentMode == ModeTypes.SingleUser && CurrentDataSource == DataSourceTypes.Manual) ||
                (string.IsNullOrEmpty(PropertyName) && CurrentDataSource == DataSourceTypes.UserProfile))
            {
                Label lblConfiguration = new Label();

                lblConfiguration.Text = "WebPart is not configured correctly.";
                this.Controls.Add(lblConfiguration);

                return;
            }



           //  string userId = "-1115182474179307609";

             try
            {


                 List<PushPin> pushPins = PushPins;

                 Random random = new Random();

                 string guidMapVarName = "map" + random.Next();
                 string guidMapFunctionName = "mapfunction" + random.Next();
                 string guidDivId = "divid" + random.Next();

            Literal l = new Literal();

         //   l.Text = "<iframe src='http://www.google.com/latitude/apps/badge/api?user=-1115182474179307609&type=iframe&maptype=roadmap' width='180' height='300' frameborder='0'></iframe>";

            string script = "<script type=\"text/javascript\" src=\"http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3\"></script><script type=\"text/javascript\">";
            
            script += string.Format("var {0} = null;function {1}() {{{0} = new VEMap('{2}');{0}.SetCredentials('{3}');",
                guidMapVarName, guidMapFunctionName, guidDivId, BingMapsKey);

            if (pushPins.Count == 0)
                script += string.Format("{0}.LoadMap();", guidMapVarName);
            else
                script += string.Format("{0}.LoadMap(new VELatLong({1}, {2}, 0, VEAltitudeMode.RelativeToGround), 10, {3}, false, VEMapMode.Mode2D, true, 1);",
                    guidMapVarName, pushPins[0].Latitude.ToString(), pushPins[0].Longitude.ToString(), MapOptions.Find(t => t.Type == CurrentMapType).ToString());

            foreach (PushPin pushPin in pushPins)
            {
                string guidPolygon = "polygon" + random.Next();

                script += string.Format("var {4} = new VEShape(VEShapeType.Pushpin, new VELatLong({0},{1}));{4}.SetTitle('{2}');{4}.SetDescription('{3}');",
               pushPin.Latitude, pushPin.Longitude, pushPin.Name, "Updated: " + pushPin.Date.ToString(), guidPolygon);

                if(!string.IsNullOrEmpty(pushPin.PictureURL))
                {
                    string guidCustomPin = "custompin" + random.Next();

                    script += string.Format("var {0} = new VECustomIconSpecification();{0}.Image = '{1}';{2}.SetCustomIcon({0});",
                        guidCustomPin, pushPin.PictureURL, guidPolygon); 
                }

                script += string.Format("{0}.AddShape({1})", guidMapVarName, guidPolygon);

             }

        script += "}</script>";



            l.Text = script;
            
            this.Page.Header.Controls.Add(l);
            
            Literal ll = new Literal();

            ll.Text = string.Format("<div id='{0}' style=\"position:relative; width:{1}px; height:{2}px;\"></div>  <script type=\"text/javascript\">{3}();</script> ", guidDivId, MapWidth, MapHeight, guidMapFunctionName);


            this.Controls.Add(ll);
             }
             catch (Exception e)
             {
                 Label l = new Label();
                 l.Text = e.ToString();
                 this.Controls.Add(l);
             }


        }

        public List<DataSource> DataSources(ModeTypes modeType)
        {
            List<DataSource> dataSources = new List<DataSource>();



            switch (modeType)
            {
                case ModeTypes.SingleUser:

                    dataSources.Add(new DataSource()
                    {
                        Type = DataSourceTypes.Manual,
                        Name = "Manual"
                    });


                    break;
                case ModeTypes.MultipleUser:

                    dataSources.Add(new DataSource()
                    {
                        Type = DataSourceTypes.List,
                        Name = "List"
                    });

                    break;
            }

            dataSources.Add(new DataSource()
            {
                Type = DataSourceTypes.UserProfile,
                Name = "User Profile"
            });



            return dataSources;
        }


        public class DataSource
        {
            public string Name { get; set; }
            public DataSourceTypes Type { get; set; }

            public DataSource()
            {
            }
        }

        public enum DataSourceTypes
        {
            Manual,
            List,
            UserProfile
        }



        public List<Mode> Modes
        {
            get
            {
                List<Mode> modes = new List<Mode>();

                modes.Add(new Mode()
                {
                    Type = ModeTypes.SingleUser,
                    Name = "Single user"
                });

                modes.Add(new Mode()
                {
                    Type = ModeTypes.MultipleUser,
                    Name = "Multiple users"
                });

                return modes;

            }
        }

        public class Mode
        {
            public string Name { get; set; }
            public ModeTypes Type { get; set; }

            public Mode()
            {
            }
        }


        public enum ModeTypes
        {
            SingleUser,
            MultipleUser
        }


        public List<MapType> MapOptions
        {
            get
            {
                List<MapType> types = new List<MapType>();

                types.Add(new MapType()
                {
                    Type = MapTypes.Road,
                });

                types.Add(new MapType()
                {
                    Type = MapTypes.Aerial,
                });

                return types;

            }
        }

        public class MapType
        {
           // public string Name { get; set; }
            public MapTypes Type { get; set; }

            public MapType()
            {
            }

            public override string ToString()
            {
                return string.Format("VEMapStyle.{0}", Type);
            }
        }


        public enum MapTypes
        {
            Road,
            Aerial
        }





        public List<UserType> UserOptions
        {
            get
            {
                List<UserType> userTypes = new List<UserType>();

                userTypes.Add(new UserType()
                {
                    ID = UserTypes.Current.ToString(),
                    Name = "Current user"
                });

                userTypes.Add(new UserType()
                {
                    ID = UserTypes.SiteOwner.ToString(),
                    Name = "Site owner (MySite)"
                });

                SPUserCollection users = SPContext.Current.Site.RootWeb.AllUsers;

                Microsoft.Office.Server.UserProfiles.UserProfileManager userProfileManager = new Microsoft.Office.Server.UserProfiles.UserProfileManager(SPServiceContext.Current);

                foreach(SPUser user in users)
                {
                    if(userProfileManager.UserExists(user.LoginName))
                    {
                        userTypes.Add(new UserType()
                        {
                            ID = user.LoginName,
                            Name = user.Name
                        });
                    }
                    
                }

                return userTypes;

            }
        }

        public class UserType
        {
            public string Name { get; set; }
            public string ID { get; set; }

            public UserType()
            {
            }

        }


        public enum UserTypes
        {
            Current,
            SiteOwner,
            Manual
        }





        public List<UsersType> UsersOptions
        {
            get
            {
                List<UsersType> usersTypes = new List<UsersType>();

                usersTypes.Add(new UsersType()
                {
                    Type = UsersTypes.Site,
                    Name = "Site"
                });

                usersTypes.Add(new UsersType()
                {
                    Type  = UsersTypes.Group,
                    Name = "Group"
                });


                return usersTypes;

            }
        }

        public class UsersType
        {
            public string Name { get; set; }
            public UsersTypes Type { get; set; }

            public UsersType()
            {
            }

        }


        public enum UsersTypes
        {
            Site,
            Group
        }









        public List<Site> Sites
        {
            get
            {
                List<Site> sites = new List<Site>();

                SPWebCollection webs = SPContext.Current.Site.AllWebs;
                
                
                foreach (SPWeb web in webs)
                {
                    
                    sites.Add(new Site()
                    {
                        Title = web.Title,
                        ID = web.ID
                        
                    });
                }

                return sites;

            }
        }

        public new class Site
        {
            public string Title { get; set; }
            public Guid ID { get; set; }

            public Site()
            {
            }

        
        }

        public List<List> Lists(Guid siteId)
        {
            List<List> lists = new List<List>();

            using (SPWeb web = SPContext.Current.Site.OpenWeb(siteId))
            {
                SPListCollection spLists = web.Lists;

                foreach (SPList list in spLists)
                {
                    lists.Add(new List()
                    {
                        Title = list.Title,
                        ID = list.ID
                    });
                }
            }

            return lists;
        }

        public class List
        {
            public string Title { get; set; }
            public Guid ID { get; set; }

            public List()
            {
            }
        }

        public List<Group> Groups(Guid siteId)
        {
            List<Group> groups = new List<Group>();

            using (SPWeb web = SPContext.Current.Site.OpenWeb(siteId))
            {
                SPGroupCollection spGroups = web.Groups;
                
                foreach (SPGroup group in spGroups)
                {
                    groups.Add(new Group()
                    {
                        Name = group.Name,
                        ID = group.ID
                    });
                }
            }

            return groups;
        }

        public class Group
        {
            public string Name { get; set; }
            public int ID { get; set; }

            public Group()
            {
            }
        }



        public List<Field> Fields(Guid siteId, Guid listId)
        {
            List<Field> fields = new List<Field>();

            using (SPWeb web = SPContext.Current.Site.OpenWeb(siteId))
            {

                SPList list = web.Lists[listId];

                foreach (SPField field in list.Fields)
                {
                    if (field.Type == SPFieldType.Text || field.Type == SPFieldType.Choice)
                        fields.Add(new Field()
                        {
                            Title = field.Title,
                            ID = field.Id
                        });
                }
            }
            return fields;

        }

        public class Field
        {
            public string Title { get; set; }
            public Guid ID { get; set; }

            public Field()
            {
            }
        }

    }
}
